package waal.pc.backend.module;

import waal.pc.backend.Log;
import waal.pc.backend.Utility;
import waal.pc.backend.channels.AnalogChannel;
import waal.pc.backend.channels.AnalogOutputChannel;
import waal.pc.backend.channels.Channel;

import waal.pc.backend.hub.Hub;

public class ModuleAnalog extends Module {

	private double multi;
	private int transferBytes = 2; 
	private int answerBytes = 0;
	private boolean longData = false;
	
	private AnalogChannel[] allChannels;
	private AnalogChannel[] inChannels;
	private AnalogOutputChannel[] outChannels;
	private AnalogChannel[] measurementChannels;

	public ModuleAnalog(Hub hub, int number, boolean active, String name) {
		super(hub, number, active, name, 0xCF);
		
	}
	
	public void initModule(boolean active, int channels, int activeChannels, int inOut, int bits, long minTimeBase, double min, double max, String unit, String[] channelNames) {
		this.initModule(active, channels, bits, minTimeBase, min, max, unit);
		Log.addToDebug(6, "ModuleAnalog", "initModule", " new Modules with "+ channels + " channels and Strings: " + channelNames.length);
		if(bits >= 16) {
			this.longData = true;
			this.transferBytes = 4;
		}
		this.multi = (max - min)/(Math.pow(2, bits)-1);
		this.allChannels = new AnalogChannel[channels];
		this.inChannels = new AnalogChannel[channels]; 
		this.outChannels = new AnalogOutputChannel[channels];
		
		int mask;
		for(int i = 0; i < channels; i++) {
			mask = (1 << i);
			if((activeChannels & mask) > 0) {
				this.activeChannels[i] = true;
			} else {
				this.activeChannels[i] = false;
			}
				
			
			if((inOut & mask ) > 0) {	
				this.hasInput = true;
				this.input[i] = true;
				this.inChannels[i] = new AnalogChannel(this, i, channelNames[i]);
				this.inChannelSet.add(this.inChannels[i]);
				this.allChannelSet.add(this.inChannels[i]);
				this.allChannels[i] = this.inChannels[i];
			} else {
				this.hasOutput = true;
				this.input[i] = false;
				this.outChannels[i] = new AnalogOutputChannel(this, i, channelNames[i]);
				this.outChannelSet.add(this.outChannels[i]);
				this.allChannelSet.add(this.outChannels[i]);
				this.allChannels[i] = this.outChannels[i];
			}

		}
		this.claculateAnswerLength();
		this.initilized = true;
		this.fireModuleEvent(5);
	}
	
	@Override
	public boolean isDigital() {
		return false;
	}
	
	public int getAnswerBytes() {
		//TODO
		return this.answerBytes;
	}
	
	public void processAnswer(double time, int[] values) {
		if(this.longData) {
			this.processAnswer32Bit(time, values);
		} else {
			this.processAnswer16Bit(time, values);
		}
	}
	
	private void processAnswer16Bit(double time, int[] values) {
		double dvalue;
		int vi = 0;
		for(int i = 0; i < this.measurementChannels.length; i++) {
			dvalue = (double) this.multi * (values[vi++] + (values[vi++] <<8));
			//Log.addToDebug(2, "ModuleAnalog", "processAnswer16Bit", "Module: " + this.number + " 16Bit for Channel " + this.activeInChannels[i].getChannelNumber() + "/" + this.activeInChannels.length +  " result: " + dvalue);
			this.measurementChannels[i].addValue(time, dvalue);
		}
	}
		
	private void processAnswer32Bit(double time, int[] values) {
		double dvalue;
		for(int i = 0; i < this.measurementChannels.length; i++) {
			dvalue = (double) this.multi * (values[i++] + (values[i++] << 8) + (values[i++] << 16) + (values[i++] << 24)); 
			this.measurementChannels[i].addValue(time, dvalue);
		}
	}

	
	@Override
	public void processConfig(int[] data) {
		//int ch = data[2];
		switch(data[3]) {
		case 0xFA: //Channel On/Off
			this.setChannelActive(data[4]);
			break;
		case 0xFC:
			if(this.transferBytes == 2) {
				this.setChannel16BitValue(data[2], data[4], data[5]);
			} else {
				this.setChannel32BitValue(data[2], data[4], data[5], data[6], data[7]);
			}
			break;	
		}
	}
	
	public void sendChannelActive(int number, boolean enable) {
		if(enable) {
			this.SendConfig(number, 0xFF, 1);
		} else {
			this.SendConfig(number, 0xFF, 0);
		}
	}
	
	public void sendChannelValue(int number, double value) {
		//System.out.println("Hello " + value);
		if(value < this.min) {
			value = min;
		} else if(value > this.max) {
			value = max;
		}
		int iValue = (int) (value / this.multi);
		if(this.transferBytes == 2) {
			Log.addToDebug(2, "ModuleAnalog", "sendChannelValue", "Send DAC 16Bit: " + iValue);
			this.SendConfig(number, 0xFE, Utility.intTo2Bytes(iValue));
		} else {
			Log.addToDebug(2, "ModuleAnalog", "sendChannelValue", "Send DAC 16Bit: " + iValue);
			this.SendConfig(number, 0xFD, Utility.intTo4Bytes(iValue));
			
		}
	}
	
	public void sendChannelStatusRequest(int number) {
		this.SendConfig(number, 0xFC);
	}
	
	
	private void setChannelActive(int value) {
		for(int i = 0; i < this.allChannels.length; i++) {
			if((value & (1 << i)) > 0) {
				this.setChannelActive(i, true);
			} else {
				this.setChannelActive(i, false);
			}
		}
	}
	
	private void setChannelActive(int ch, boolean value) {
		if(value != this.isChannelActive(ch)) {
			this.activeChannels[ch] = value;
			this.claculateAnswerLength();
		}
	}
	
	private void setChannel16BitValue(int ch, int i1, int i2 ) {
		this.setChannelValue(ch, ((long) i1 + (i2 << 8)));
	}
	
	private void setChannel32BitValue(int ch, int i1, int i2, int i3, int i4 ) {
		
		this.setChannelValue(ch, Utility.fourIntToLong(i1, i2, i3, i4));
	}
	
	private void setChannelValue(int ch, long value ) {
		
	}
	
	
	private void claculateAnswerLength() {
		int i;
		int activeIn = 0;
		AnalogChannel[] activeInCh = new AnalogChannel[8];
		for(i = 0; i < this.allChannels.length; i++) {
			if(this.activeChannels[i]) {
				activeInCh[activeIn++] = this.allChannels[i];
			}
		}
		this.measurementChannels = new AnalogChannel[activeIn];
		for(i = 0; i < this.measurementChannels.length; i++) {
			this.measurementChannels[i] = activeInCh[i];
		}
		this.answerBytes = activeIn * this.transferBytes;


	
	}

	@Override
	public void setChannelName(int ch, String name) {
		this.allChannels[ch].setName(name);
	}

	@Override
	public void updateValues() {
		// TODO Auto-generated method stub
		
	}
	
	@Override
	public Channel getChannel(int number) {
		//TODO Check
		return this.allChannels[number];
	}	
}
